/*
 * Created by Jaren at 2022/2/21 17:45
 */
import 'dart:typed_data';

import 'package:inmotion_protocol/common/exceptions.dart';
import 'package:inmotion_protocol/common/file.dart';
import 'package:inmotion_protocol/lorin/bean/common/date.dart';
import 'package:inmotion_protocol/util/byte_util.dart';

class LorinFile extends File {
    static const String fileSeparator = '/';

  static const int typeFile = 0;
  static const int typeDir = 1;

  static final LorinFile invalidFile = LorinFile();
  static const int invalidFileBytesLength = 10;

  /// 标识文件/文件夹的标识
  int _type = typeDir;

  int size = 0;
  Datetime? lastUpdate;

  /// 当前文件/文件夹的完整路径
  String path = '';

  /// 文件名，不包含路径
  String name = '';

  int childrenBytes = 0;

  LorinFile();

  static LorinFile create(int type, String path) {
    if (path == '') {
      throw InvalidParamException("Empty path is not allowed!");
    }

    if (type != typeDir && path.endsWith(fileSeparator)) {
      throw InvalidParamException("'" + path + "' ends with file separator but type[${type.toString()}] is not DIR.");
    }

        if (type == typeDir && !path.endsWith(fileSeparator)) {
            throw InvalidParamException("Type is DIR but path '" + path + "' is not end with file separator.");
        }
        LorinFile file = LorinFile();

        file._type = type;
        file.path = _formatPath(path);

        int index = file.path.substring(0, file.path.length - 1).lastIndexOf(fileSeparator);
        file.name = index < 0 ? '' : file.path.substring(index + 1, file.path.length);
        if (file.name == '') file.name = file.path;
        return file;
    }

    bool isDirectory() {
        return _type == typeDir;
    }

    bool isFile() {
        return _type == typeFile;
    }


    String getParent() {
        String tmpPath;
        if (path.endsWith(fileSeparator)) {
            tmpPath = path.substring(0, path.length - 1);
        } else {
            tmpPath = path;
        }
        int index = tmpPath.lastIndexOf(fileSeparator);
        if (index < 0) {
            return '';
        }
        return path.substring(0, index + 1 /* 保留路径分隔符 */);
    }

    int getChildrenBytes() {
        return childrenBytes;
    }

    void setChildrenBytes(int childrenBytes) {
        this.childrenBytes = childrenBytes;
    }

    /// 格式化path<br/>
    /// 若path为目录且以分隔符结尾，则去掉末尾分隔符<br/>
    /// 特别地，根目录/不处理，原样返回
    ///
    /// @param path 待格式化的path
    /// @return 格式化后的path，
    static String _formatPath(String path) {
        if (path == '') {
            return path;
        }
//        if (path.charAt(path.length() - 1) == FILE_SEPARATOR && path.length() != 1) {
////            return path.substring(0, path.length() - 1);
////        }
        return path;
    }

    int bytesLength() {
        if (_type == typeFile) {
            // 文件名长度(1 byte) + 文件名 + 文件名结束符\0 1 byte + 文件大小(4 bytes) + 修改日期(4 bytes)
            return 1 + name.length + 1 + 4 + 4;
        }

        /* 文件组名长度1 byte + 文件组名 + 文件组名\0 1 byte + 文件组中包含的文件数1 byte */
        return 1 + path.length + 1 + 1;
    }

    String getAbsolutePath() {
        return path;
    }

    @override
    String toString() {
        if (this == invalidFile) return "";
        return "File{" +
            "type=" + (_type == typeFile ? "file" : "dir") +
            ", size=" + size.toString() +
            ", lastUpdate=" + lastUpdate.toString() +
            ", path='" + path + '\'' +
            '}';
    }

    static LorinFile fileFromBytes(Uint8ClampedList bytes, int offset, String parent, List<File> output) {
        if (offset + 1 >= bytes.length) return invalidFile;

    int filenameLength = bytes[offset];
    if (offset + filenameLength + 4 + 4 >= bytes.length) {
      // 确定length后是否有足够数据组成一个完整文件信息包
      return invalidFile;
    }
    if (filenameLength <= 1) {
      // 文件名为空，无效文件
      return invalidFile;
    }
    offset++;
    String filename = String.fromCharCodes(bytes, offset, offset + filenameLength - 1);
    LorinFile file = LorinFile.create(typeFile, parent + filename);
    offset += filenameLength;
    file.size = ByteUtil.bytes2UIntLe(bytes, offset, 4);
    offset += 4;
    Datetime lastUpdate = Datetime();
    Datetime.fromBytes(bytes, offset: offset, output: lastUpdate);
    file.lastUpdate = lastUpdate;

    output.add(file);
    return file;
  }

  static LorinFile dirFromBytes(Uint8ClampedList bytes, int offset, List<File> output) {
        if (offset + 1 >= bytes.length) return invalidFile;

        int pathNameLength = bytes[offset++];
        if (offset + pathNameLength + 1 >= bytes.length || pathNameLength < 1) {
            // offset + path name length + file count(1 byte)
            // 确定length后是否有足够数据
            return invalidFile;
        }
        String path = String.fromCharCodes(bytes, offset, offset + pathNameLength - 1 /* path name中包含\0，去掉\0 */);
        offset += pathNameLength;

        // current dir obj
        LorinFile file = LorinFile.create(LorinFile.typeDir, path);

        int fileCount = ByteUtil.bytes2UIntLe(bytes, offset, 1);

        if (fileCount == 0) {
            return file;
        }

        offset += 1;

        int childCount = 0;

        LorinFile child;
        while (childCount < fileCount && (child = LorinFile.fileFromBytes(bytes, offset, path, output)) != invalidFile) {
            int childBytesLength = child.bytesLength();
            offset += childBytesLength;
            childCount++;
            file.setChildrenBytes(file.getChildrenBytes() + childBytesLength);
        }

        return file;
    }
}